package com.yjzx.bean;

import com.yjzx.interfaces.CommonClasses;
import com.yjzx.interfaces.ModifierEnum;
import com.yjzx.util.CollectionUtil;
import com.yjzx.util.StringUtils;
import javassist.*;

import java.io.File;
import java.util.List;

/**
 * @author yjzx
 * @date 2023/10/22
 * @description
 */

public class UpdateClass extends JavassistInfo {
    /**
     * class全路径名
     */
    private String className;
    /**
     * 修饰词
     */
    private List<String> modifiers;
    /**
     * 参数
     */
    private List<UpdateParam> params;
    /**
     * 构造器
     */
    private Boolean enableNoParamConstructor = true;
    private List<UpdateConstructor> constructors;
    /**
     * 注解
     */
    private List<UpdateAnnotate> annotates;

    private List<UpdateInterface> interfaces;

    private String superClassName;
    /**
     * 方法
     */
    private List<UpdateMethod> methods;

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public List<String> getModifiers() {
        return modifiers;
    }

    public void setModifiers(List<String> modifiers) {
        this.modifiers = modifiers;
    }

    public List<UpdateParam> getParams() {
        return params;
    }

    public void setParams(List<UpdateParam> params) {
        this.params = params;
    }

    public Boolean getEnableNoParamConstructor() {
        return enableNoParamConstructor;
    }

    public void setEnableNoParamConstructor(Boolean enableNoParamConstructor) {
        this.enableNoParamConstructor = enableNoParamConstructor;
    }

    public List<UpdateConstructor> getConstructors() {
        return constructors;
    }

    public void setConstructors(List<UpdateConstructor> constructors) {
        this.constructors = constructors;
    }

    public List<UpdateAnnotate> getAnnotates() {
        return annotates;
    }

    public void setAnnotates(List<UpdateAnnotate> annotates) {
        this.annotates = annotates;
    }

    public List<UpdateMethod> getMethods() {
        return methods;
    }

    public void setMethods(List<UpdateMethod> methods) {
        this.methods = methods;
    }

    public List<UpdateInterface> getInterfaces() {
        return interfaces;
    }

    public void setInterfaces(List<UpdateInterface> interfaces) {
        this.interfaces = interfaces;
    }

    public String getSuperClassName() {
        return superClassName;
    }

    public void setSuperClassName(String superClassName) {
        this.superClassName = superClassName;
    }

    public CtClass updateClass(ClassPool pool, File printPathFile) {

        CtClass ctClass = classloader(pool);
        try {
            //添加修饰符
            ctClass.setModifiers(ModifierEnum.modifierToInt(this.getModifiers()));
            //添加注解
            if (CollectionUtil.isNotEmpty(this.annotates)) {
                //把这个对象怼到要打上这个注解的字段/类上面
                this.annotates.forEach(x -> {
                    ctClass.getClassFile().addAttribute(x.toAnnotationAttribute(pool, ctClass));
                });
            }
            //参数
            if (CollectionUtil.isNotEmpty(this.params)) {
                for (UpdateParam param : this.params) {
                    CtField ctField = param.toField(pool, ctClass);
                    if (StringUtils.isNotBlank(param.getDefaultValue())) {
                        ctClass.addField(param.toField(pool, ctClass), param.getDefaultValue());
                    } else {
                        ctClass.addField(param.toField(pool, ctClass));
                    }
                    if (param.isEnabledGet() == null || param.isEnabledGet()) {
                        String prefix = "java.lang.Boolean".equals(param.getFieldClassName()) ? "is" : "get";

                        ctClass.addMethod(CtNewMethod.getter(prefix + StringUtils.headUpperCase(param.getParamName()), ctField));
                    }
                    if (param.isEnabledSet() == null || param.isEnabledSet()) {
                        ctClass.addMethod(CtNewMethod.setter("set" + StringUtils.headUpperCase(param.getParamName()), ctField));
                    }
                }
            }
            ctClass.debugWriteFile(printPathFile.getPath());
            if (CollectionUtil.isNotEmpty(this.constructors)) {
                for (UpdateConstructor constructor : this.constructors) {
                    ctClass.addConstructor(constructor.toConstructor(pool, ctClass));
                }
            }
            if (this.enableNoParamConstructor!=null&&!this.enableNoParamConstructor) {
                for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
                    // 判断构造器是否为无参构造器
                    if (constructor.getParameterTypes().length == 0) {
                        // 如果是无参构造器，则修改构造器的访问权限为私有
                        constructor.setModifiers(Modifier.PRIVATE);
                    }
                }
            }
            if (StringUtils.isNotBlank(this.superClassName)) {
                ctClass.setSuperclass(CommonClasses.getCtClass(pool, this.superClassName));
            }
            if (CollectionUtil.isNotEmpty(this.interfaces)) {
                for (UpdateInterface updateInterface : this.interfaces) {
                    ctClass.addInterface(updateInterface.toInterface(pool, ctClass));
                }
            }
            //方法
            if (CollectionUtil.isNotEmpty(this.methods)) {
                for (UpdateMethod method : this.methods) {
                    ctClass.addMethod(method.toMethod(pool, ctClass));
                }
            }
            ctClass.debugWriteFile(printPathFile.getPath());

        } catch (CannotCompileException | NotFoundException e) {
            throw new RuntimeException(e);
        }
        return ctClass;

    }

    public CtClass classloader(ClassPool pool) {
        CtClass ctClass = null;
        NotFoundException exception = null;
        try {
            ctClass = pool.get(CommonClasses.classNameConvert(this.className));
        } catch (NotFoundException e) {
            exception = e;
        }
        if (exception != null && NotFoundException.class.equals(exception.getClass())) {
            ctClass = pool.makeClass(this.className);
        }
        if (ctClass == null) {
            throw new RuntimeException("未能正确获取" + this.className + exception, exception);
        }
        return ctClass;
    }

    @Override
    public void check() {
        this.checkParam(this.className);
    }
}